1 module hip.hiprenderer.backend.metal.mtlvertex; 2 3 version(AppleOS): 4 import metal; 5 import hip.hiprenderer; 6 import hip.error.handler; 7 import hip.hiprenderer.backend.metal.mtlrenderer; 8 import hip.hiprenderer.backend.metal.mtlshader; 9 10 11 MTLResourceOptions mtlOptions(HipBufferUsage usage) 12 { 13 final switch(usage) 14 { 15 case HipBufferUsage.DYNAMIC: 16 return MTLResourceOptions.StorageModeShared; 17 case HipBufferUsage.STATIC: 18 return MTLResourceOptions.StorageModePrivate; 19 case HipBufferUsage.DEFAULT: 20 return MTLResourceOptions.DefaultCache; 21 } 22 } 23 24 25 __gshared MTLBuffer boundIndexBuffer; 26 class HipMTLIndexBuffer : IHipIndexBufferImpl 27 { 28 MTLBuffer buffer; 29 MTLResourceOptions options; 30 MTLCommandQueue cmdQueue; 31 MTLDevice device; 32 33 this(MTLDevice device, MTLCommandQueue cmdQueue, size_t length, HipBufferUsage usage) 34 { 35 this.device = device; 36 this.cmdQueue = cmdQueue; 37 options = usage.mtlOptions; 38 buffer = device.newBuffer(length*index_t.sizeof, options); 39 buffer.retain(); 40 } 41 void bind() 42 { 43 boundIndexBuffer = buffer; 44 } 45 46 void unbind() 47 { 48 if(boundIndexBuffer is buffer) boundIndexBuffer = null; 49 } 50 51 void setData(const index_t[] data) 52 { 53 if(options == MTLResourceOptions.StorageModePrivate) 54 { 55 MTLBuffer temp = device.newBuffer(data.ptr, data.length*index_t.sizeof, MTLResourceOptions.StorageModeShared); 56 MTLCommandBuffer cmdBuffer = cmdQueue.defaultCommandBuffer(); 57 MTLBlitCommandEncoder cmdEncoder = cmdBuffer.blitCommandEncoder; 58 cmdEncoder.copyFromBuffer(temp, 0, buffer, 0, data.length*index_t.sizeof); 59 cmdEncoder.endEncoding(); 60 cmdBuffer.commit(); 61 cmdBuffer.waitUntilCompleted(); 62 } 63 else 64 { 65 if(buffer) buffer.release(); 66 buffer = device.newBuffer(data.ptr, data.length*index_t.sizeof, options); 67 buffer.retain(); 68 } 69 } 70 71 void updateData(int offset, const index_t[] data) 72 { 73 buffer.contents[offset..offset+data.length*index_t.sizeof] = cast(void[])(data[]); 74 } 75 } 76 77 class HipMTLVertexBuffer : IHipVertexBufferImpl 78 { 79 MTLBuffer buffer; 80 MTLCommandQueue cmdQueue; 81 MTLResourceOptions options; 82 MTLDevice device; 83 84 this(MTLDevice device, MTLCommandQueue cmdQueue, size_t size, HipBufferUsage usage) 85 { 86 this.device = device; 87 this.cmdQueue = cmdQueue; 88 options = usage.mtlOptions; 89 buffer = device.newBuffer(size, options); 90 buffer.retain(); 91 } 92 void bind(){} 93 void unbind(){} 94 void setData(const void[] data) 95 { 96 if(options == MTLResourceOptions.StorageModePrivate) 97 { 98 MTLBuffer temp = device.newBuffer(data.ptr, data.length, MTLResourceOptions.StorageModeShared); 99 MTLCommandBuffer cmdBuffer = cmdQueue.defaultCommandBuffer(); 100 MTLBlitCommandEncoder cmdEncoder = cmdBuffer.blitCommandEncoder; 101 cmdEncoder.copyFromBuffer(temp, 0, buffer, 0, data.length); 102 cmdEncoder.endEncoding(); 103 cmdBuffer.commit(); 104 cmdBuffer.waitUntilCompleted(); 105 if(cmdBuffer.error) 106 NSLog("Command Buffer Error: %@".ns, cmdBuffer.error); 107 } 108 else 109 { 110 if(buffer) buffer.release(); 111 buffer = device.newBuffer(data.ptr, data.length, options); 112 buffer.retain(); 113 } 114 } 115 116 void updateData(int offset, const void[] data) 117 { 118 buffer.contents[offset..offset+data.length] = data[]; 119 } 120 } 121 122 MTLVertexFormat mtlVertexFormatFromAttributeInfo(HipVertexAttributeInfo i) 123 { 124 final switch(i.valueType) 125 { 126 case HipAttributeType.Rgba32: return MTLVertexFormat.uchar4Normalized; 127 case HipAttributeType.Float: 128 final switch(i.count) 129 { 130 case 1: return MTLVertexFormat.float1; 131 case 2: return MTLVertexFormat.float2; 132 case 3: return MTLVertexFormat.float3; 133 case 4: return MTLVertexFormat.float4; 134 } 135 case HipAttributeType.Int: 136 final switch(i.count) 137 { 138 case 1: return MTLVertexFormat.int1; 139 case 2: return MTLVertexFormat.int2; 140 case 3: return MTLVertexFormat.int3; 141 case 4: return MTLVertexFormat.int4; 142 } 143 case HipAttributeType.Uint: 144 final switch(i.count) 145 { 146 case 1: return MTLVertexFormat.uint1; 147 case 2: return MTLVertexFormat.uint2; 148 case 3: return MTLVertexFormat.uint3; 149 case 4: return MTLVertexFormat.uint4; 150 } 151 case HipAttributeType.Bool: 152 final switch(i.count) 153 { 154 case 1: return MTLVertexFormat.char1; 155 case 2: return MTLVertexFormat.char2; 156 case 3: return MTLVertexFormat.char3; 157 case 4: return MTLVertexFormat.char4; 158 } 159 } 160 assert(false, "Unknown format"); 161 } 162 163 class HipMTLVertexArray : IHipVertexArrayImpl 164 { 165 MTLVertexDescriptor descriptor; 166 HipMTLVertexBuffer vBuffer; 167 HipMTLIndexBuffer iBuffer; 168 169 HipMTLRenderer mtlRenderer; 170 MTLDevice device; 171 172 this(MTLDevice device, HipMTLRenderer mtlRenderer) 173 { 174 this.device = device; 175 this.mtlRenderer = mtlRenderer; 176 descriptor = MTLVertexDescriptor.vertexDescriptor(); 177 descriptor.layouts[1].stepFunction = MTLVertexStepFunction.PerVertex; 178 descriptor.layouts[1].stepRate = 1; 179 } 180 181 void bind(IHipVertexBufferImpl vbo, IHipIndexBufferImpl ebo) 182 { 183 if(vbo is null || ebo is null) 184 return; 185 vBuffer = cast(HipMTLVertexBuffer)vbo; 186 iBuffer = cast(HipMTLIndexBuffer)ebo; 187 vbo.bind(); 188 ebo.bind(); 189 mtlRenderer.getEncoder.setVertexBuffer(vBuffer.buffer, 0, 1); 190 } 191 192 void unbind(IHipVertexBufferImpl vbo, IHipIndexBufferImpl ebo) 193 { 194 mtlRenderer.getEncoder.setVertexBuffer(null, 0, 1); 195 vbo.unbind(); 196 ebo.unbind(); 197 vBuffer = null; 198 iBuffer = null; 199 } 200 201 /** 202 * HipMTLRenderer will ALWAYS assume that: 203 * Buffer 0 is for uniforms 204 * Buffer 1 is for vertex attributes 205 */ 206 void setAttributeInfo(ref HipVertexAttributeInfo info, uint stride) 207 { 208 descriptor.layouts[1].stride = stride; 209 MTLVertexAttributeDescriptor attribute = descriptor.attributes[info.index]; 210 attribute.format = mtlVertexFormatFromAttributeInfo(info); 211 attribute.offset = info.offset; 212 attribute.bufferIndex = 1; 213 } 214 215 void createInputLayout(Shader s) 216 { 217 HipMTLShaderProgram shader = (cast(HipMTLShaderProgram)s.shaderProgram); 218 shader.createInputLayout(device, descriptor); 219 } 220 }